package com.qdlc.p2p.dal.common;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Service;
import org.tuckey.web.filters.urlrewrite.utils.StringUtils;

import com.qdlc.p2p.common.core.redis.JedisCache;
import com.qdlc.p2p.common.core.redis.JedisCacheFactory;
import com.qdlc.p2p.common.util.DataUtils;
import com.qdlc.p2p.dal.dao.SystemConfigDao;
import com.qdlc.p2p.dal.dto.SystemConfig;

/**
 * SystemConfig配置文件工具类，提供对s_config表记录中常用配置项的操作。
 * 该类需要在其他Bean实例化之前进行实例化，获取BeanFactory对象。因为在其他Bean中可能以静态变量的形式调用了{@link SystemConfig#getValue(String)}方法。
 * 考虑到兼容以前nid字段有些有"con_"，有些没有，对前缀进行了处理。
 *
 * @author linya 2016-01-26
 */
@Service
public class SystemConfigHelper implements BeanFactoryPostProcessor {

    /** 对应的nid，前缀，见s_config表 */
    private static final String PREFIX = "con_";

    /** 加载的Spring容器 */
    private static BeanFactory context;

    /** 定义缓存对象 */
    private static JedisCache<SystemConfig> cache;

    /** 定义缓存keys集合 */
    private static Set<String> nidSet = new HashSet<String>();

    /** 保存nid是否有前缀"con_"映射 */
    private static Map<String, Boolean> nidPrefixMaps = new HashMap<String, Boolean>();

    /** 读写锁，用于缓存更新时 */
    private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    /** 是否已经被完全初始化 */
    private static boolean inited = false;

    //定义所有Nid类型，具体类型参见s_config表记录
    public enum Nid {
        VERSION("version"),
        ADMIN_ACCOUNT("admin_account"),
        WEB_NAME("web_name"),
        TPP_PLATFORM("tpp_platform"),
        TPP_NAME("tpp_name"),
        ACCOUNT_TYPE("account_type"),
        IMAGE_SERVER_URL("image_server_url"),
        INVESTOR_MANAGE_FEE("investor_manage_fee"),
        MEMBER_EMAIL("member_email"),
        WEB_URL("web_url"),
        VERIFY_CODE_TIME("verify_code_time"),
        RAS_PUBLIC_KEY("rsa_public"),
        CHECK_SIGN_KEY("check_sign_key"),
        CON_ENCRYPT("con_encrypt"),
        //红包规则校验
        RED_PACKETS_RULE("red_packets_rule"),
        //新浪手续费
        CASH_SERV_FEE("cash_serv_fee"),
        //平台手续费
        WEB_CACH_FEE("web_cash_fee"),
        //逾期利息划分比例
        LATE_INTEREST_RATE("late_interest_rate"),
        CASH_SINGLE_DAY_TIME_LIMIT("cash_single_day_time_limit"),
        CASH_SINGLE_DAY_MAX_AMOUNT_LIMIT("cash_single_day_max_amount_limit"),
        CASH_FEE_COUNT_MIN("cash_fee_count_min"),
        COMP_CASH_SINGLE_MAX_AMOUNT_LIMIT("comp_cash_single_max_amount_limit"),
        CASH_SINGLE_MAX_AMOUNT_LIMIT("cash_single_max_amount_limit"),
        CASH_SINGLE_MIN_AMOUNT_LIMIT("cash_single_min_amount_limit"),
        IS_PAY_CASH("is_pay_cash"),
        MERCHANT_ID("merchant_id"),
        VIP_FEE("vip_fee"),
        MIAO_TENDER_DAY_LIMIT("miao_tender_day_limit"),
        FTL_DIR("ftl_dir"),
        CUSTOMER_HOTLINE("customer_hotline"),
        COPYRIGHT("copyright"),
        WEB_RECORD_NUMBER("web_record_number"),
        //获取平台是否自己垫付充值手续费
        RECHARGE_WEB("recharge_web"),
        RECHARGE_FEE_RATE("recharge_fee_rate"),
        RECHARGE_FEE("recharge_fee"),
        VIP_PRIORITY("vip_priority"),
        CON_BORROW_AJAXTENDERLISTPAGENUM("con_borrow_ajaxtenderlistpagenum"),
        OVERDUE_FEE("overdue_fee"),
        //？天内免逾期罚息
        FREE_OVERDUE_DAY("free_overdue_day"),
        ALL_FREE_OVERDUE_DAY("all_free_overdue_day"),
        RED_ENVELOPE_EXCHANGE_LIMIT("red_envelope_exchange_limit"),
        WINE_COUPONS_EXCHANGE_LIMIT("wine_coupons_exchange_limit"),
        SMS_USERNAME("sms_username"),
        SMS_PASSWORD("sms_password"),
        USE_SMS_URL("sms_use_url"),
        USED_SMS_URL("sms_used_url"),
        FUND_CODE("fund_code"),
        COMPANY_FILE_PATH("company_file_path"),
        GOODS_KEY("goods_key"),
        REQUEST_URL("request_url"),
        RETURN_URL("return_url"),
        CONFIG_ONLINE("config_online"),
        SFTP_HOST("sftp_host"),
        SFTP_PORT("sftp_port"),
        YJF_KEY("yjf_key"), BANK_NUM("bank_num"),
        MANAGE_URL("manage_url"),
        SINGLE_CLIENT_LOGIN("single_client_login"),
        SINGLE_LOGIN("single_login"),
        LOTTERY_OPEN("lottery_open"),
        LOGIN_URL("login_url"),
        YIELD_LIST("yield_list"),
        SINGLE_MAX_AMOUNT_LIMIT("single_max_amount_limit"),
        ADDRESS("address"),
        ADMIN_URL("admin_url"),
        DISTINCTION_TYPE("distinction_type"),
        USER_TENDER_REDPACKAGE_RATE("user_tender_redpackage_rate"),
        CHANNEL_TITLE("channel_title"),
        CHANNEL_ID("channel_id"),
        CHANNEL_KEY("channel_key"),
        CHANNEl_REG("channel_reg"),
        SELL_MOVIE_CUST_ID("sell_movie_cust_id"),
        REQUEST_PHP_DOMAIN("request_php_domain"),
        EXCHANGE_TICKET_PLATFORM_WEB("exchange_ticket_platform_web"),
        COOPERATION_INTERFACE("cooperation_interface"),
        BY_MONTH_TENDER_ACTIVE("by_month_tender_active"),
        WECHAT_URL("weChat_url"),

        LOG_UNFREE("log_unfree"),
        //邀请绑卡人活动发放数量
        INVITE_BINDING_CARD_COUNT("invite_binding_card_count");
        
        String nid;

        Nid(String nid) {
            this.nid = nid;
        }

        public String getNid() {
            return nid;
        }

    }

    public static SystemConfig getSystemConfig(Nid nid) {
        SystemConfig config = getFromCacheOrDB(nid.getNid(), true);
        return config == null ? new SystemConfig() : config;
    }

    /**
     * 根据nid获取配置值
     *
     * @param nid
     * @return
     */
    public static String getValue(String nid) {
        return getValueFromCacheOrDB(nid,true);
    }

    public static String getValue(Nid nid) {
        return getValue(nid.getNid());
    }

    public static long getLongValue(String nid) {
        String value = getValue(nid);
        return DataUtils.toLong(value);
    }

    public static long getLongValue(Nid nid) {
        String value = getValue(nid);
        return DataUtils.toLong(value);
    }

    public static int getIntegerValue(String nid) {
        String value = getValue(nid);
        return DataUtils.toInt(value);
    }

    public static int getIntegerValue(Nid nid) {
        String value = getValue(nid);
        return DataUtils.toInt(value);
    }

    public static double getDoubleValue(String nid) {
        String value = getValue(nid);
        return DataUtils.toDouble(value);
    }

    public static double getDoubleValue(Nid nid) {
        String value = getValue(nid);
        return DataUtils.toDouble(value);
    }

    public static String getVersion() {
        return getValue(Nid.VERSION);
    }

    /**
     * 根据nid获取配置状态
     *
     * @param nid
     * @return
     */
    public static int getStatus(String nid) {
        SystemConfig config = getFromCacheOrDB(nid, true);
        return config == null ? 0 : config.getStatus();
    }

    public static int getStatus(Nid nid) {
        SystemConfig config = getFromCacheOrDB(nid.getNid(), true);
        return config == null ? 0 : config.getStatus();
    }

    /**
     * 更新单个配置文件
     *
     * @param nid
     */
    public static void update(String nid) {
        getFromCacheOrDB(nid, false);
    }

    /**
     * 缓存key值，去掉前缀
     *
     * @param nid
     * @return
     */
    private static WrapNid wrapNid(String nid) {
        if (nid.startsWith(PREFIX)) {
            return new WrapNid(nid.substring(PREFIX.length()), true);
        }
        return new WrapNid(nid, false);
    }

    /**
     * 从缓存或者数据库中获取数据
     * 如果cached=true，先从缓存中查找，如果没有，然后从数据库查找。
     * 如果cached=false，强制从数据库获取，并将获取的数据存放到缓存中去。
     *
     * @param nid
     * @param cached
     * @return
     */
    private static SystemConfig getFromCacheOrDB(String nid, boolean cached) {
        //首次调用初始化缓存对象，以及从数据库批量加载好配置
        if (!inited) {
            cache = JedisCacheFactory.newInstance(SystemConfig.class, context);
            initial(context);
            inited = true;
        }
        SystemConfig config = null;
        try {
            lock.readLock().lock();
            //如果有前缀，则去掉前缀
            WrapNid wrapNid = wrapNid(nid);
            //获得缓存key
            String key = wrapNid.cacheKey;
            //尝试从缓存中获取
            if (cached) {
                config = cache.get(key);
            }
            //如果缓存中获取不到，需要查询数据库
            if (config == null) {
                //nid有没有前缀，是在初始化的时候就已经保存了，所以直接使用nidPrefixMaps来获取
                SystemConfigDao configDao = context.getBean(SystemConfigDao.class);
                boolean hasPrifix = DataUtils.toBoolean(nidPrefixMaps.get(key));
                String nidName = (hasPrifix) ? PREFIX + key : key;
                //从数据库查询，然后放入缓存
                config = configDao.findObjByProperty("nid", nidName);
                cache.set(key, config);
            }
        } finally {
            lock.readLock().unlock();
        }
        return config;
    }

    /**
     * 初始化配置信息
     *
     * @param context
     */
    private static void initial(BeanFactory context) {
        SystemConfigDao configDao = context.getBean(SystemConfigDao.class);
        List<SystemConfig> list = configDao.findAll();
        if (list == null || cache == null) {
            return;
        }
        for (SystemConfig config : list) {
            String nid = config.getNid();
            if (StringUtils.isBlank(nid)) {
                continue;
            }
            //保存缓存key和数据库中的nid是否有前缀
            WrapNid wrapNid = wrapNid(config.getNid());
            String key = wrapNid.cacheKey;
            nidSet.add(key);
            nidPrefixMaps.put(key, wrapNid.hasPrefix);
            cache.set(key, config);
        }
    }

    public static Set<String> getNidSet() {
        return nidSet;
    }

    @Override
    public void postProcessBeanFactory(
            ConfigurableListableBeanFactory beanFactory) throws BeansException {
        context = beanFactory;
    }

    /**
     * 包装nid值，如果数据库中的nid值有前缀，则去掉前缀作为缓存key，并标记是否有前缀
     *
     * @author linya
     */
    private static class WrapNid {
        String cacheKey;
        boolean hasPrefix;

        WrapNid(String cacheKey, boolean hasPrefix) {
            this.cacheKey = cacheKey;
            this.hasPrefix = hasPrefix;
        }
    }

    /**
     *  修改增加取值从Cache或者Db中取
     *
     * @author hongbinglin
     */
    private static String getValueFromCacheOrDB(String nid, boolean cached){
        SystemConfig config = getFromCacheOrDB(nid, cached);
        if (config == null) {
            return "";
        }
        return DataUtils.toString(config.getValue()).trim();
    }

    /**
     * 从DB中取
     *
     * @param nid
     * @return
     */
    public static String getValueFromDB(String nid) {
        return getValueFromCacheOrDB(nid,false);
    }

}
